transition這個翻譯成過渡的意思,一個吸引人的圖表當中,加入了一點動畫成分和過渡的轉變能吸引使用者的目光,也讓觀看者有預期心理畫面將要轉換,另外也使得圖表更有質感。
首先我們先創建一個<rect>
以寬50高300的長方形。
const svg = d3.select("body")
.append("svg")
.attr("width",800)
.attr("height",800);
svg.append("rect")
.attr("x",10)
.attr("y",0)
.attr("width",50)
.attr("height",300)
.attr("fill","orange");
官方文件如下圖
這時候我們要考慮的地方是transition()
要放在到哪個位置,依據官方的文件說明,前面要有一個selection,也就是你所選擇的元素,後方需要帶入要改變的樣式,因此依據指示我們可以放在append()
的後面
程式碼如下
svg.append("rect")
.attr("x",10)
.attr("y",0)
.attr("width",50)
.attr("height",300)
.attr("fill","orange")
;
此時當你按下瀏覽器的重新整理按鈕的時候或是剛載入網頁的時候應當會看到如下面這個範例
codepen範例如下
D3Day13-1範例
但出現的速度如此之快因此我們可以加入另一個函式來處理播放時間。
duration()
添加至transtition()
後面,以下範例設定3000毫秒,也就是3秒
程式碼如下
const svg = d3.select("body")
.append("svg")
.attr("width",600)
.attr("height",600);
svg.append("rect")
.transition()
.duration(3000)
.attr("x",10)
.attr("y",0)
.attr("width",50)
.attr("height",300)
.attr("fill","orange");
codepen範例如下
delay()
函式放在transition()
函式後面,主要的用途是要延遲幾秒再執行的意思
codepen範例如下
這邊主要是帶入要呈現漸變時期的動畫時的效果,裡面帶入參數第一個是指定你要的函式第二個是時間,具體用法程式碼如下
svg.append("rect")
.transition()
.ease(d3.easeLinear,1)
.duration(1500)
.attr("x",10)
.attr("y",0)
.attr("width",50)
.attr("height",300)
.attr("fill","orange");
函數圖形如下
這邊帶入的就是線性的動畫,雖然官方文件寫參數值帶入t,但筆者觀看原始碼後線性所轉回的值與原先是一樣的情況,基本上你帶入99所呈現的畫面應當不會有所差別。
codepen範例如下
Linear原始碼如下
這邊官方說明就有提到t數值會改變動畫的效果
官方原文如下
程式碼如下
svg.append("rect")
.transition()
.ease(d3.easePoly,4)
.duration(5000)
.attr("x",10)
.attr("y",0)
.attr("width",50)
.attr("height",300)
.attr("fill","orange")
;
codepen範例如下
函數圖型如下
這邊僅介紹兩個函數當範例
其他更多的函數可以參考官方API文件
值得注意的一點是有些函數並不適用於每個屬性像是這個長條圖有寬和高
因此如果你想嘗試使用d3.easeElasticInOut()
這個函數的話
由於函數圖型有超出原本的區間,因此會造成寬高變成負值就會輸出錯誤。
另外D3的作者有發表關於其他範例,可以參考d3作者所用的範例,它所用的範例是改變位移的屬性,因此也就能更加瞭解每個函式直接的差別。
範例如下
官方範例網址如下
原本的<rect>
的程式碼如下,延續我們之前的範例,假設我們希望畫面載入的時候長條圖由底部開始漸漸變長的話,該如何撰寫呢?
svg.selectAll("rect")
.data(newTaipei)
.join("rect")
.attr("x", (d, i) => {
return padding + i * 60;
})
.attr("y", (d) => {
return scaleY(d.people_total);
})
.attr("width", 50)
.attr("height", (d) => {
return 400 - scaleY(d.people_total);
})
.attr("fill", "orange");
由於transition方法鏈
後面所帶入的東西是你要改變的屬性,因此勢必我們得思考後面帶入的東西是什麼?因為我們預計會改變長方形的高度,所以我們必須關注height屬性,另外我們的svg的(0,0)座標是在左上角,如果只改變height
屬性的話,長方形的動畫會從上方伸長到下方,因此我們也得改變y
的起始點位置,這邊svg的底部y
的位置是400
,所以在transition前面寫下.attr("y", 400)
,高度從0開始伸長所以寫下.attr("height", 0)
而我們原本的程式碼是放在transition
之後當作最後的結果,最後的程式碼就如下面所示
svg.selectAll("rect")
.data(newTaipei)
.join("rect")
.attr("x", (d, i) => {
return padding + i * 60;
})
.attr("y", 400)
.attr("height", 0)
.attr("width", 50)
.attr("fill", "orange")
.transition()
.duration(3000)
.attr("y", (d) => {
return scaleY(d.people_total);
})
.attr("height", (d) => {
return 400 - scaleY(d.people_total);
});
整個做出長條圖從無到有的過程大概分成三個階段
transition
前面transition
函式和delay
與duration
等等函式transition
後面是最後要改變的屬性或樣式本日完整程式碼如下
let newTaipei = taipei.map((el) => {
el.people_total = Number(el.people_total);
el.area = Number(el.area);
el.population_density = Number(el.population_density);
el.site_id = el.site_id.substr(3);
return el;
});
let padding = 50;
let svg = d3
.select("body")
.append("svg")
.attr("width", 800)
.attr("height", 450);
let min = d3.min(newTaipei, (d) => d.people_total);
let max = d3.max(newTaipei, (d) => d.people_total);
console.log(newTaipei);
let scaleY = d3.scaleLinear().domain([0, 320000]).range([400, 0]);
svg.selectAll("rect")
.data(newTaipei)
.join("rect")
.attr("x", (d, i) => {
return padding + i * 60;
})
.attr("y", 400)
.attr("height", 0)
.attr("width", 50)
.attr("fill", "orange")
.transition()
.duration(3000)
.attr("y", (d) => {
return scaleY(d.people_total);
})
.attr("height", (d) => {
return 400 - scaleY(d.people_total);
});
svg.selectAll("text")
.data(newTaipei)
.join("text")
.text((d) => {
return d.site_id;
})
.attr("x", (d, i) => {
return padding + i * 60;
})
.attr("y", (y) => {
return 450 - 20;
});
let axisY = d3.axisRight(scaleY)
.ticks(5)
.tickFormat(function (d) {
return d / 10000 + "萬";
});
let g = svg.append("g");
axisY(g);
});
實際效果如下
頁面如下
githubPage